#include <defs.h>
#include <mmu.h>
#include <memlayout.h>
#include <clock.h>
#include <trap.h>
#include <x86.h>
#include <stdio.h>
#include <assert.h>
#include <console.h>
#include <vmm.h>
#include <swap.h>
#include <kdebug.h>

#define TICK_NUM 100

static void print_ticks() {
    cprintf("%d ticks\n",TICK_NUM);
#ifdef DEBUG_GRADE
    cprintf("End of Test.\n");
    panic("EOT: kernel seems ok.");//panic 是一个用于处理内核崩溃的函数，它会打印出错误信息并导致系统停止运行。
#endif
}

/* *
 * Interrupt descriptor table:
 *
 * Must be built at run time because shifted function addresses can't
 * be represented in relocation records.
 * */
//这行代码定义了一个名为 idt 的静态数组，其类型为 struct gatedesc，长度为 256。
//gatedesc 通常表示中断门描述符，包含有关中断的各种信息
static struct gatedesc idt[256] = {{0}};

//静态变量 idt_pd，它的类型为 struct pseudodesc。这个结构通常用于描述 IDT 的位置和大小，以便 CPU 知道如何加载 IDT。
static struct pseudodesc idt_pd = {
    sizeof(idt) - 1, (uintptr_t)idt
};

/* idt_init - initialize IDT to each of the entry points in kern/trap/vectors.S */
void
idt_init(void) {
     /* LAB1 YOUR CODE : STEP 2 */
     /* (1) Where are the entry addrs of each Interrupt Service Routine (ISR)?
      *     All ISR's entry addrs are stored in __vectors. where is uintptr_t __vectors[] ?
      *     __vectors[] is in kern/trap/vector.S which is produced by tools/vector.c
      *     (try "make" command in lab1, then you will find vector.S in kern/trap DIR)
      *     You can use  "extern uintptr_t __vectors[];" to define this extern variable which will be used later.
      * (2) Now you should setup the entries of ISR in Interrupt Description Table (IDT).
      *     Can you see idt[256] in this file? Yes, it's IDT! you can use SETGATE macro to setup each item of IDT
      * (3) After setup the contents of IDT, you will let CPU know where is the IDT by using 'lidt' instruction.
      *     You don't know the meaning of this instruction? just google it! and check the libs/x86.h to know more.
      *     Notice: the argument of lidt is idt_pd. try to find it!
      */

    extern uintptr_t __vectors[];//声明了一个外部数组 __vectors，该数组存储中断服务例程（ISR）的地址。
    int i;
    for (i = 0; i < sizeof(idt) / sizeof(struct gatedesc); i ++) {
        SETGATE(idt[i], 0, GD_KTEXT, __vectors[i], DPL_KERNEL);
    }
    //宏用于配置每个 IDT 条目.0 表示最高特权级（内核级）GD_KTEXT: 指向内核代码段的选择子，确保 ISR 在内核代码段中执行。
	//__vectors[i]: 对应中断的 ISR 地址,DPL_KERNEL: 描述符特权级，表示该中断只能由内核级代码触发。
    // set for switch from user to kernel
    //SETGATE 这行代码特别设置了 T_SWITCH_TOK（一个特定的中断向量，用于用户态到内核态的切换）的 IDT 条目。
    //DPL_USER 表示该中断可以由用户态代码触发
    SETGATE(idt[T_SWITCH_TOK], 0, GD_KTEXT, __vectors[T_SWITCH_TOK], DPL_USER);
	// load the IDT
    //使用 lidt 指令将 IDT 描述符加载到 CPU 中
    lidt(&idt_pd);
}

static const char *
trapname(int trapno) {
    static const char * const excnames[] = {
        "Divide error",
        "Debug",
        "Non-Maskable Interrupt",
        "Breakpoint",
        "Overflow",
        "BOUND Range Exceeded",
        "Invalid Opcode",
        "Device Not Available",
        "Double Fault",
        "Coprocessor Segment Overrun",
        "Invalid TSS",
        "Segment Not Present",
        "Stack Fault",
        "General Protection",
        "Page Fault",
        "(unknown trap)",
        "x87 FPU Floating-Point Error",
        "Alignment Check",
        "Machine-Check",
        "SIMD Floating-Point Exception"
    };
    //如果 trapno 小于数组长度，则返回对应的异常名称。
    if (trapno < sizeof(excnames)/sizeof(const char * const)) {
        return excnames[trapno];
    }
    //如果 trapno 在 IRQ_OFFSET 和 IRQ_OFFSET + 16 之间，表示它是一个硬件中断
    if (trapno >= IRQ_OFFSET && trapno < IRQ_OFFSET + 16) {
        return "Hardware Interrupt";
    }
    return "(unknown trap)";
}

/* trap_in_kernel - test if trap happened in kernel */
bool
trap_in_kernel(struct trapframe *tf) {
    return (tf->tf_cs == (uint16_t)KERNEL_CS);
    //函数通过检查 tf 中的 tf_cs 字段来判断当前处于哪个特权级,tf_cs 存储了当前代码段选择子的值
    //当 tf->tf_cs 等于 KERNEL_CS 时，表示陷阱发生在内核模式下
}
//数组中的字符串分别表示 IA-32 架构中的不同标志位：
static const char *IA32flags[] = {
    "CF", NULL, "PF", NULL, "AF", NULL, "ZF", "SF",
    "TF", "IF", "DF", "OF", NULL, NULL, "NT", NULL,
    "RF", "VM", "AC", "VIF", "VIP", "ID", NULL, NULL,
};
//struct trapframe *tf，一个指向 trapframe 结构的指针，包含有关陷阱发生时的 CPU 状态的信息。
void
print_trapframe(struct trapframe *tf) {
    cprintf("trapframe at %p\n", tf); //打印陷阱框架地址
    print_regs(&tf->tf_regs); //打印寄存器状态
    //打印数据段（DS）、扩展段（ES）、文件段（FS）、通用段（GS）的值。
    cprintf("  ds   0x----%04x\n", tf->tf_ds);
    cprintf("  es   0x----%04x\n", tf->tf_es);
    cprintf("  fs   0x----%04x\n", tf->tf_fs);
    cprintf("  gs   0x----%04x\n", tf->tf_gs);
    // 打印陷阱号（trap number）及其对应的名称，通过调用 trapname 函数获取。
    cprintf("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
    cprintf("  err  0x%08x\n", tf->tf_err);// 如果有错误代码，打印该字段的值。
    cprintf("  eip  0x%08x\n", tf->tf_eip);//打印当前执行的指令指针（EIP），指向出错或中断的指令。
    cprintf("  cs   0x----%04x\n", tf->tf_cs);//打印代码段寄存器（CS）的值。
    cprintf("  flag 0x%08x ", tf->tf_eflags);// 打印标志寄存器（EFLAGS）的值
    //使用循环遍历 IA32flags 数组，j 表示当前标志位的位掩码。
    int i, j;
    for (i = 0, j = 1; i < sizeof(IA32flags) / sizeof(IA32flags[0]); i ++, j <<= 1) {
        if ((tf->tf_eflags & j) && IA32flags[i] != NULL) {
            cprintf("%s,", IA32flags[i]);
        }
    }
    //通过位掩码 FL_IOPL_MASK 获取和打印当前的 I/O 特权级别。
    cprintf("IOPL=%d\n", (tf->tf_eflags & FL_IOPL_MASK) >> 12);
    //如果陷阱不是在内核中发生的（通过 trap_in_kernel 判断），
    //则打印栈指针（ESP）和栈段（SS）寄存器的值。
    if (!trap_in_kernel(tf)) {
        cprintf("  esp  0x%08x\n", tf->tf_esp);
        cprintf("  ss   0x----%04x\n", tf->tf_ss);
    }
}
//定义了一个名为 print_regs 的函数，
//打印出存储在 struct pushregs 结构体中的寄存器值。
void
print_regs(struct pushregs *regs) {
    cprintf("  edi  0x%08x\n", regs->reg_edi);
    cprintf("  esi  0x%08x\n", regs->reg_esi);
    cprintf("  ebp  0x%08x\n", regs->reg_ebp);
    cprintf("  oesp 0x%08x\n", regs->reg_oesp);//打印旧的栈指针（OESP），这个寄存器通常在陷阱或中断发生时用于记录上一个栈指针。
    cprintf("  ebx  0x%08x\n", regs->reg_ebx);
    cprintf("  edx  0x%08x\n", regs->reg_edx);
    cprintf("  ecx  0x%08x\n", regs->reg_ecx);
    cprintf("  eax  0x%08x\n", regs->reg_eax);
}
/**
 * 打印关于页面故障的详细信息。
 *
 * 此函数用于输出页面故障的详细信息，包括故障地址、访问类型（读/写）、访问模式（用户/内核）以及故障类型（未找到页面/保护故障）。
 *
 * @param tf 指向 trapframe 结构的指针，包含故障发生时的寄存器状态和错误代码。
 */
static inline void
print_pgfault(struct trapframe *tf) {
    /* error_code:
     * bit 0 == 0 means no page found, 1 means protection fault
     * bit 1 == 0 means read, 1 means write
     * bit 2 == 0 means kernel, 1 means user
     * */
    /* error_code:
     * bit 0 == 0 表示未找到页面，1 表示保护故障
     * bit 1 == 0 表示读操作，1 表示写操作
     * bit 2 == 0 表示内核模式，1 表示用户模式
     * */
    cprintf("page fault at 0x%08x: %c/%c [%s].\n", rcr2(),
            (tf->tf_err & 4) ? 'U' : 'K',
            (tf->tf_err & 2) ? 'W' : 'R',
            (tf->tf_err & 1) ? "protection fault" : "no page found");
}

/**
 * 页面故障处理函数
 * 
 * 此函数负责处理页面故障异常，它首先打印出页面故障的信息，
 * 然后根据是否有有效的内存管理结构来决定是否进行页面故障处理，
 * 如果没有有效的内存管理结构，则引发系统崩溃
 * 
 * @param tf 指向陷阱帧的指针，包含故障发生时的CPU状态信息
 * @return 返回页面故障处理的结果，或者在无法处理时引发系统崩溃
 */
static int
pgfault_handler(struct trapframe *tf) {
    // 声明一个外部变量，用于检查内存管理结构
    extern struct mm_struct *check_mm_struct;
    // 打印页面故障信息
    print_pgfault(tf);
    // 检查是否存在有效的内存管理结构
    if (check_mm_struct != NULL) {
        // 如果存在，调用页面故障处理函数
        return do_pgfault(check_mm_struct, tf->tf_err, rcr2());
    }
    // 如果没有有效的内存管理结构，引发系统崩溃
    panic("unhandled page fault.\n");
}

static volatile int in_swap_tick_event = 0;
extern struct mm_struct *check_mm_struct;

struct trapframe switchk2u, *switchu2k;
//定义了一个名为 trap_dispatch 的静态函数，根据发生的陷阱类型进行相应的处理。
// 参数 tf 是指向陷阱帧的指针，包含了关于陷阱发生时的CPU状态信息。
static void
trap_dispatch(struct trapframe *tf) {
    char c;

    int ret;
    //通过 switch 语句根据 tf->tf_trapno 的值来分发不同的陷阱处理逻辑。
    switch (tf->tf_trapno) {
    case T_PGFLT:  //page fault
    // 处理页故障中断
        if ((ret = pgfault_handler(tf)) != 0) {
            print_trapframe(tf);
            panic("handle pgfault failed. %e\n", ret);
        }
        break;
    case IRQ_OFFSET + IRQ_TIMER:
#if 0
    LAB3 : If some page replacement algorithm(such as CLOCK PRA) need tick to change the priority of pages, 
    then you can add code here. 
#endif
        /* LAB1 YOUR CODE : STEP 3 */
        /* handle the timer interrupt */
        /* (1) After a timer interrupt, you should record this event using a global variable (increase it), such as ticks in kern/driver/clock.c
         * (2) Every TICK_NUM cycle, you can print some info using a funciton, such as print_ticks().
         * (3) Too Simple? Yes, I think so!
         */
        ticks ++; //记录中断事件
        if (ticks % TICK_NUM == 0)
        {
            print_ticks();
        }//每经过 TICK_NUM 次周期时，调用 print_ticks() 打印信息。
        break;
    //处理串口中断，调用 cons_getc() 从串口读取字符并打印。
    case IRQ_OFFSET + IRQ_COM1:
        c = cons_getc();
        cprintf("serial [%03d] %c\n", c, c);
        break;
    //处理键盘中断，调用 cons_getc() 读取键盘输入并打印。
    case IRQ_OFFSET + IRQ_KBD:
        c = cons_getc();
        cprintf("kbd [%03d] %c\n", c, c);
        break;
    //LAB1 CHALLENGE 1 : YOUR CODE you should modify below codes.
    case T_SWITCH_TOU://表示发生了从内核模式切换到用户模式的请求。
        if (tf->tf_cs != USER_CS) {//判断当前是否在内核模式下
            switchk2u = *tf; //保存当前陷阱框架
            switchk2u.tf_cs = USER_CS;//设置用户模式的段寄存器
            //将数据段和栈段寄存器 都设置为 USER_DS（用户数据段）
            switchk2u.tf_ds = switchk2u.tf_es = switchk2u.tf_ss = USER_DS;
            switchk2u.tf_esp = (uint32_t)tf + sizeof(struct trapframe) - 8;
        
            // set eflags, make sure ucore can use io under user mode.
            // if CPL > IOPL, then cpu will generate a general protection.
            switchk2u.tf_eflags |= FL_IOPL_MASK;//允许用户模式下进行 I/O 操作
        
            // set temporary stack
            // then iret will jump to the right stack
            *((uint32_t *)tf - 1) = (uint32_t)&switchk2u;
        }
        break;
    case T_SWITCH_TOK://T_SWITCH_TOK 表示发生了从用户模式切换到内核模式的请求。
        if (tf->tf_cs != KERNEL_CS) { //判断当前是否在用户模式下
            tf->tf_cs = KERNEL_CS;
            tf->tf_ds = tf->tf_es = KERNEL_DS;
            //设置内核模式的段寄存器
            tf->tf_eflags &= ~FL_IOPL_MASK; //清除 I/O 权限标志
            switchu2k = (struct trapframe *)(tf->tf_esp - (sizeof(struct trapframe) - 8));
            //使用 memmove 将当前的陷阱框架（除了最后8个字节）复制到新的陷阱框架位置 switchu2k
            memmove(switchu2k, tf, sizeof(struct trapframe) - 8);
            //将新的陷阱框架地址 switchu2k 存储到当前陷阱框架之前的一个栈位置
            *((uint32_t *)tf - 1) = (uint32_t)switchu2k;
        }
        break;
    //系统接收到这两个中断信号时，不执行任何操作，直接跳过。
    case IRQ_OFFSET + IRQ_IDE1:
    case IRQ_OFFSET + IRQ_IDE2:
        /* do nothing */
        break;
    default:
        // in kernel, it must be a mistake
        //检查当前陷阱框架的代码段寄存器 tf->tf_cs 的特权级
        //(tf->tf_cs & 3) == 0 检查是否在内核模式中
        if ((tf->tf_cs & 3) == 0) {
            print_trapframe(tf);
            panic("unexpected trap in kernel.\n");
        }
    }
}

/* *
 * trap - handles or dispatches an exception/interrupt. if and when trap() returns,
 * the code in kern/trap/trapentry.S restores the old CPU state saved in the
 * trapframe and then uses the iret instruction to return from the exception.
 * */
void
trap(struct trapframe *tf) {
    // dispatch based on what type of trap occurred
    //该行代码调用 trap_dispatch 函数，将陷阱帧传递给它。
    trap_dispatch(tf);
}

